package com.summer.data.spring;

import com.summer.data.Jsql;
import com.summer.data.connection.Session;
import com.summer.data.connection.Utils;
import com.summer.data.connection.session.AbstractSession;
import com.summer.data.connection.source.DataSourceSessionFactory;
import com.summer.data.dialect.Dialect;
import com.summer.data.exception.SqlException;
import com.summer.data.logging.Logger;
import com.summer.data.rs.Extractor;
import com.summer.data.rs.extractor.KeysExtractor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementSetter;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author 345480567@qq.com
 */
public class SessionTemplate extends AbstractSession implements Session {

    org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());

    private final JdbcTemplate jdbcTemplate;

    public SessionTemplate(DataSource dataSource) {
        super(new DataSourceSessionFactory(dataSource));
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public SessionTemplate(JdbcTemplate jdbcTemplate) {
        super(new DataSourceSessionFactory(jdbcTemplate.getDataSource()));
        this.jdbcTemplate = jdbcTemplate;
    }

    private Connection connection;

    @Override
    public Connection getConnection() {
        try {
            if (connection == null) {
                connection = jdbcTemplate.getDataSource().getConnection();
            }
            return connection;
        } catch (SQLException e) {
            throw new SqlException(e);
        }
    }

    @Override
    public void close() {
        if (connection != null) {
            Utils.closeConnection(connection);
            connection = null;
        }
    }

    @Override
    public Dialect getDialect() {
        return sessionFactory.getDialect();
    }

    @Override
    public <T> T query(Jsql jsql, Extractor<T> extractor) {
        Dialect dialect = getDialect();

        if (Logger.isLogSql()) {
            jsql.print(dialect);
        }

        PreparedStatementCreator psc = new JsqlPreparedStatementCreator(jsql, null);

        PreparedStatementSetter pss = ps -> {
            Utils.prepare(ps, dialect, jsql);
        };

        ResultSetExtractorAdapter<T> rse = new ResultSetExtractorAdapter<>(extractor, dialect);

        return jdbcTemplate.query(psc, pss, rse);
    }

    @Override
    public Integer executeUpdate(Jsql jsql, KeysExtractor keysExtractor) {
        Dialect dialect = getDialect();

        if (Logger.isLogSql()) {
            jsql.print(dialect);
        }

        JsqlPreparedStatementCreator jsqlPsc = new JsqlPreparedStatementCreator(jsql, keysExtractor);
        PreparedStatementCallback<Integer> action = ps -> {
            int num = 0;

            Utils.prepare(ps, dialect, jsql);
            num = ps.executeUpdate();

            if (keysExtractor != null) {
                ResultSet rs = null;
                rs = ps.getGeneratedKeys();
                if (rs.next()) {
                    keysExtractor.setResult(num);
                    keysExtractor.fetchKeys(rs, dialect);
                }
            }

            return num;
        };
        return jdbcTemplate.execute(jsqlPsc, action);
    }

}
